package com.immediatus.engine.modifier;

import com.immediatus.engine.contracts.IModifier;

import java.util.Arrays;

public class ParallelModifier<T> extends BaseModifier<T> implements IModifier.IModifierListener<T>{
    private float _secondsElapsed;
    private final float _duration;
    private final IModifier<T>[] _modifiersmModifiers;
    private boolean _finishedCached;

    public ParallelModifier(final IModifier<T>... modifiers_) throws IllegalArgumentException{
        this(null, modifiers_);
    }

    public ParallelModifier(final IModifierListener<T> modifierListener_, final IModifier<T>... modifiers_) throws IllegalArgumentException{
        super(modifierListener_);
        if (modifiers_.length == 0){
            throw new IllegalArgumentException("modifier must not be empty");
        }
        Arrays.sort(modifiers_, MODIFIER_COMPARATOR_DURATION_DESCENDING);
        this._modifiersmModifiers = modifiers_;

        final IModifier<T> modifierWithLongestDuration = modifiers_[0];
        this._duration = modifierWithLongestDuration.getDuration();
        modifierWithLongestDuration.addModifierListener(this);
    }

    protected ParallelModifier(final ParallelModifier<T> parallelModifier_){
        final IModifier<T>[] otherModifiers = parallelModifier_._modifiersmModifiers;
        this._modifiersmModifiers = new IModifier[otherModifiers.length];

        final IModifier<T>[] modifiers = this._modifiersmModifiers;
        for (int i = modifiers.length - 1; i >= 0; i--){
            modifiers[i] = otherModifiers[i].deepCopy();
        }

        final IModifier<T> modifierWithLongestDuration = modifiers[0];
        this._duration = modifierWithLongestDuration.getDuration();
        modifierWithLongestDuration.addModifierListener(this);
    }

    @Override
    public ParallelModifier<T> deepCopy(){
        return new ParallelModifier<T>(this);
    }

    @Override
    public float getSecondsElapsed(){
        return this._secondsElapsed;
    }

    @Override
    public float getDuration(){
        return this._duration;
    }

    @Override
    public float onUpdate(final float secondsElapsed_, final T item_){
        if (this.isFinished()){
            return 0;
        } else{
            float secondsElapsedRemaining = secondsElapsed_;

            final IModifier<T>[] shapeModifiers = this._modifiersmModifiers;

            this._finishedCached = false;
            while (secondsElapsedRemaining > 0 && !this._finishedCached){
                float secondsElapsedUsed = 0;
                for (int i = shapeModifiers.length - 1; i >= 0; i--){
                    secondsElapsedUsed = Math.max(secondsElapsedUsed, shapeModifiers[i].onUpdate(secondsElapsed_, item_));
                }
                secondsElapsedRemaining -= secondsElapsedUsed;
            }
            this._finishedCached = false;

            final float secondsElapsedUsed = secondsElapsed_ - secondsElapsedRemaining;
            this._secondsElapsed += secondsElapsedUsed;
            return secondsElapsedUsed;
        }
    }

    @Override
    public void reset(){
        this.setFinished(false);
        this._secondsElapsed = 0;

        final IModifier<T>[] shapeModifiers = this._modifiersmModifiers;
        for (int i = shapeModifiers.length - 1; i >= 0; i--){
            shapeModifiers[i].reset();
        }
    }

    @Override
    public void onModifierStarted(final IModifier<T> modifier_, final T item_){
        this.onModifierStarted(item_);
    }

    @Override
    public void onModifierFinished(final IModifier<T> modifier_, final T item_){
        this.setFinished(true);
        this._finishedCached = true;
        this.onModifierFinished(item_);
    }
}
